// Copyright 2004 The Apache Software Foundation
// 
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain token copy of the License at
// 
//     http://www.apache.org/licenses/LICENSE-2.0
// 
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

namespace AspectSharp.Core.Matchers
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using AspectSharp.Lang.AST;

	/// <summary>
	/// Summary description for DefaultAspectMatcher.
	/// </summary>
	[Serializable]
	public class DefaultAspectMatcher : IAspectMatcher
	{
        private Dictionary<Type, IClassMatcher> _customMatcherCache = new Dictionary<Type, IClassMatcher>();

		#region IAspectMatcher Members

		public AspectDefinition[] Match(Type targetType, AspectDefinitionCollection aspects)
		{
			// TODO: think about caches here...

            return (from aspect in aspects
                    where GetClassMatcher(aspect).Match(targetType, aspect)
                    select aspect).ToArray();
		}

		#endregion

		protected virtual IClassMatcher GetClassMatcher(AspectDefinition aspect)
		{
			switch (aspect.TargetType.TargetStrategy)
			{
				case TargetStrategyEnum.SingleType:
					return SingleTypeMatcher.Instance;
				case TargetStrategyEnum.Assignable:
					return AssignableMatcher.Instance;
				case TargetStrategyEnum.Namespace:
					return NamespaceMatcher.Instance;
				case TargetStrategyEnum.Custom:
					return GetCustomMatcher(aspect.TargetType);
			}
			// There is no way we can get here - hopefully
			return null;
		}

		protected virtual IClassMatcher GetCustomMatcher(TargetTypeDefinition target)
		{
			Type customType = target.CustomMatcherType.ResolvedType;

            IClassMatcher matcher = null;

            if (_customMatcherCache.TryGetValue(customType, out matcher))
                return matcher;

			try
			{
				matcher = (IClassMatcher) Activator.CreateInstance( customType );
			}
			catch(InvalidCastException ex)
			{
				throw new MatcherException("Error trying to cast your custom class matcher to IClassMatcher", ex);
			}
			catch(Exception ex)
			{
				throw new MatcherException("Error trying to instantiate your custom class matcher", ex);
			}

            _customMatcherCache[customType] = matcher;

			return matcher;
		}

		
	}
}
